package com.zhanghe.im;

import com.zhanghe.im.decoder.PacketDecoder;
import com.zhanghe.im.encoder.PacketEncoder;
import com.zhanghe.im.handler.*;
import com.zhanghe.im.console.ConsoleCommandManager;
import com.zhanghe.im.console.LoginConsoleCommand;
import com.zhanghe.im.statehandler.IMIdleStateHandler;
import com.zhanghe.im.util.SessionUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

import java.util.Scanner;
import java.util.Date;
import java.util.concurrent.TimeUnit;

public class NettyClient {

    private static final int MAX_RETRY = 5;

    public static void main(String[] args) {
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();
        bootstrap
                .group(workerGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
//                        ch.pipeline().addLast(new FirstClientHandler());
//                        ch.pipeline().addLast(new ClientHandler());
//                        ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,7,4));
                        ch.pipeline().addLast(new IMIdleStateHandler());
                        ch.pipeline().addLast(new Spliter());
                        ch.pipeline().addLast(new PacketDecoder());
                        ch.pipeline().addLast(new HeartBeatTimerHandler());
                        ch.pipeline().addLast(new SimpleLoginResponseHandler());
                        ch.pipeline().addLast(new SimpleCreateGroupResonseHandler());
                        ch.pipeline().addLast(new SimpleJoinGroupResponseHandler());
                        ch.pipeline().addLast(new SimpleQuitGroupResponseHndler());
                        ch.pipeline().addLast(new SimpleListGroupResponseHandler());
                        ch.pipeline().addLast(new SimpleMessageResponseHandler());
                        ch.pipeline().addLast(new PacketEncoder());
                    }
                });
        connect(bootstrap,"127.0.0.1",1000,MAX_RETRY);
    }

    public static void connect(final Bootstrap bootstrap,final String ip,final int port,final int retry){
        bootstrap.connect(ip,port).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if(future.isSuccess()){
                    System.out.println("连接成功");
                    Channel channel =  ((ChannelFuture) future).channel();
//                    LoginUtil.markLogin(channel);
                    startConsoleThread(channel);
                }else if(retry == 0){
                    System.out.println("重试次数已用完,放弃连接!");
                }else{
                    // 第几次重连
                    int order = (MAX_RETRY - retry) + 1;
                    // 本次重连的间隔
                    int delay = 1 << order;
                    System.out.println(new Date()+":连接失败,开始第"+order+"次重连");
                    bootstrap.config().group().schedule( ()->connect(bootstrap,ip,port+1,retry-1) ,delay,TimeUnit.SECONDS);
                }
            }
        });
    }

    private static void startConsoleThread(Channel channel){
        ConsoleCommandManager consoleCommandManager = new ConsoleCommandManager();
        LoginConsoleCommand loginConsoleCommand = new LoginConsoleCommand();
        Scanner scanner = new Scanner(System.in);
        new Thread(()->{
            while(!Thread.interrupted()){
                if(!SessionUtil.hasLogin(channel)){
                    loginConsoleCommand.exec(scanner,channel);
                    waitForLoginResponse();
                }else{
                    consoleCommandManager.exec(scanner,channel);
                }
            }
        }).start();
    }

    private static void waitForLoginResponse() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ignored) {
        }
    }

}
